package cn.xmoit.print;

import cn.hutool.core.io.resource.ClassPathResource;

import javax.print.*;
import javax.print.attribute.standard.PrinterName;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

/**
 * 采用点阵字库ts24.lib
 * <p>
 * <a href="https://blog.csdn.net/qq_34805041/article/details/103240885">Java 使用斑马打印机发送
 * ZPL 参数说明</a> <a href="http://www.doc88.com/p-7874039583708.html">斑马打印机 ZPL II 语言编程</a>
 * <a href="https://www.cnblogs.com/lab-zj/p/13984581.html">ZPL语言指令解析</a>
 * </p>
 *
 * @author fangyy
 *
 */
public class ZplPrinter {

	/**
	 * 打印机完整路径
	 */
	private final String printerUri;

	/**
	 * 打印机服务
	 */
	private PrintService printService = null;

	private byte[] dotFont;

	/**
	 * Set Darkness设置色带颜色的深度 0-30
	 */
	private final String darkness = "~SD10";

	/**
	 * Print Width打印宽度0-1500
	 */
	private final String width = "^PW1000";

	/**
	 * Label Length标签长度0-x(暂无作用)
	 */
	private final String length = "^LL800";

	/**
	 * 标签格式以^XA开始
	 */
	private String begin = "^XA" + darkness + width;

	/**
	 * 标签格式以^XZ结束
	 */
	private String end = "^XZ";

	/**
	 * 打印内容
	 */
	private String content = "";

	/**
	 * 打印的结果信息
	 */
	private String message = "";

	/**
	 * 构造方法
	 * @param printerUri 打印机路径
	 */
	public ZplPrinter(String printerUri) {
		this.printerUri = printerUri;
		// 加载字体
		try {
			ClassPathResource classPathResource = new ClassPathResource("ts24.lib");
			InputStream fis = classPathResource.getStream();
			dotFont = new byte[fis.available()];
			fis.read(dotFont);
			fis.close();
		}
		catch (FileNotFoundException e) {
			System.out.println("ts24.lib 文件不存在");
			e.printStackTrace();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		// 初始化打印机
		// AppContext.getAppContext().put(PrintServiceLookup.class.getDeclaredClasses()[0],
		// null);
		// 刷新打印机列表
		PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
		if (services != null) {
			for (PrintService service : services) {
				if (printerUri.equals(service.getName())) {
					printService = service;
					break;
				}
			}
		}
		if (printService == null) {
			System.out.println("没有找到打印机：[" + printerUri + "]");
			// 循环出所有的打印机
			if (services != null && services.length > 0) {
				System.out.println("可用的打印机列表：");
				for (PrintService service : services) {
					System.out.println("[" + service.getName() + "]");
				}
			}
		}
		else {
			System.out.println("找到打印机：[" + printerUri + "]");
			System.out.println("打印机名称：[" + printService.getAttribute(PrinterName.class).getValue() + "]");
		}
	}

	/**
	 * 设置条形码
	 * @param barcode 条码字符
	 * @param zpl 条码样式模板
	 */
	public void setBarcode(String barcode, String zpl) {
		content += zpl.replace("${data}", barcode);
	}

	/**
	 * 中文字符、英文字符(包含数字)混合
	 * @param str 中文、英文
	 * @param x x坐标
	 * @param y y坐标
	 * @param eh 英文字体高度height
	 * @param ew 英文字体宽度width
	 * @param es 英文字体间距spacing
	 * @param mx 中文x轴字体图形放大倍率。范围1-10，默认1
	 * @param my 中文y轴字体图形放大倍率。范围1-10，默认1
	 * @param ms 中文字体间距。24是个比较合适的值。
	 */
	public void setText(String str, int x, int y, int eh, int ew, int es, int mx, float my, int ms) {
		byte[] ch = str2bytes(str);
		for (int off = 0; off < ch.length;) {
			// 中文字符
			if (((int) ch[off] & 0x00ff) >= 0xA0) {
				try {
					int qCode = ch[off] & 0xff;
					int wCode = ch[off + 1] & 0xff;
					// content += String.format("^FO%d,%d^XG0000%01X%01X,%d,%d^FS\n", x,
					// y, qCode, wCode, mx, my);
					content += String.format("^FO%d,%d^XG0000%01X%01X,%d,%f^FS\n", x, y, qCode, wCode, mx, my);
					// System.out.println("content===="+content);
					begin += String.format("~DG0000%02X%02X,00072,003,\n", qCode, wCode);
					qCode = (qCode + 128 - 32) & 0x00ff;
					wCode = (wCode + 128 - 32) & 0x00ff;
					int offset = ((int) qCode - 16) * 94 * 72 + ((int) wCode - 1) * 72;
					for (int j = 0; j < 72; j += 3) {
						qCode = (int) dotFont[j + offset] & 0x00ff;
						wCode = (int) dotFont[j + offset + 1] & 0x00ff;
						int qCode1 = (int) dotFont[j + offset + 2] & 0x00ff;
						begin += String.format("%02X%02X%02X\n", qCode, wCode, qCode1);
					}
					x = x + ms * mx;
					off = off + 2;
				}
				catch (Exception e) {
					e.printStackTrace();
					// 替换成X号
					setChar("X", x, y, eh, ew);
					// 注意间距更改为英文字符间距
					x = x + es;
					off = off + 2;
				}
			}
			// 英文字符
			else if (((int) ch[off] & 0x00FF) < 0xA0) {
				setChar(String.format("%c", ch[off]), x, y, eh, ew);
				x = x + es;
				off++;
			}
		}
	}

	/**
	 * 英文字符串(包含数字)
	 * @param str 英文字符串
	 * @param x x坐标
	 * @param y y坐标
	 * @param h 高度 H
	 * @param w 宽度
	 */
	public void setChar(String str, double x, double y, int h, int w) {
		content += "^FO" + x + "," + y + "^AQN," + h + "," + w + "^FD" + str + "^FS";
	}

	/**
	 * 英文字符(包含数字)顺时针旋转180度
	 * @param str 英文字符串
	 * @param x x坐标
	 * @param y y坐标
	 * @param h 高度
	 * @param w 宽度
	 */
	public void setCharR(String str, int x, int y, int h, int w) {
		content += "^FO" + x + "," + y + "^A0I," + h + "," + w + "^FD" + str + "^FS";
	}

	/**
	 * 获取完整的ZPL
	 * @return 完整的ZPL
	 */
	public String getZpl() {
		return begin + content + end;
	}

	/**
	 * 重置ZPL指令，当需要打印多张纸的时候需要调用。
	 */
	public void resetZpl() {
		begin = "^XA" + darkness + width;
		end = "^XZ";
		content = "";
	}

	/**
	 * 打印
	 * @param zpl 完整的ZPL
	 */
	public boolean print(String zpl) {
		if (printService == null) {
			message = "打印出错：没有找到打印机[" + printerUri + "]";
			System.out.println("打印出错：没有找到打印机[" + printerUri + "]");
			return false;
		}
		DocPrintJob job = printService.createPrintJob();
		byte[] by = zpl.getBytes();
		DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
		Doc doc = new SimpleDoc(by, flavor, null);
		try {
			job.print(doc, null);
			message = "已打印";
			System.out.println("已打印");
			return true;
		}
		catch (PrintException e) {
			message = "打印出错:" + e.getMessage();
			System.out.println("打印出错:" + e.getMessage());
			e.printStackTrace();
			return false;
		}
	}

	public String getMessage() {
		return message;
	}

	/**
	 * 字符串转byte[]
	 * @param s 字符串转字节数组
	 * @return 字节数组
	 */
	private byte[] str2bytes(String s) {
		if (null == s || "".equals(s)) {
			return null;
		}
		byte[] bytes = null;
		try {
			bytes = s.getBytes("gb2312");
		}
		catch (UnsupportedEncodingException ex) {
			ex.printStackTrace();
		}
		return bytes;
	}

	/**
	 * ^B3N,Y,Y code39条形码
	 * @param p Zpl 打印
	 * @return true or false
	 */
	public boolean printBarcode(ZplPrinter p) {
		// 1.打印单个条码
		String bar0 = "D21066000101";
		String str = "厦门思明区测试部";
		// 条码样式模板
		String bar0Zpl = "^FO190,90^BY1.4,3,80^BCN,60,N,N,N^FD${data}^FS";
		p.setBarcode(bar0, bar0Zpl);

		p.setCharR(bar0, 200, 60, 25, 25);
		content += "^CI26^SEE:GB18030.DAT^CWJ,E:MSUNG24^CI28^FO160,20^A1I,10,10^FD" + str + "^FS";
		// p.setText("厦门思明区测试部", 160, 120, 10, 10, 30, 1, 0, 24);
		content += "^FO500,90^BY1.4,3,80^BCN,60,N,N,N^FD" + bar0 + "^FS^FO510,60^A0I,25,25^FD" + bar0
				+ "^FS^FT680,20^A1I,10,10^FD" + str + "^FS";
		String zpl = p.getZpl();
		System.out.println(zpl);
		// 打印
		return p.print(zpl);
	}

	public static void main(String[] args) {
		ZplPrinter p = new ZplPrinter("ZDesigner ZM400 (EPL)");
		p.printBarcode(p);

	}

}
